#include <iostream>
#include <vector>
#include "class_learn.h"

template <typename T>
T calculateAverage(const std::vector<T> &v)
{
    std::size_t size{v.size()};
    int sum{0};
    for (std::size_t i{0}; i < size; i++)
    {
        sum += v[i];
    }
    return sum / size;
}

void iterator_learn()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::cout << calculateAverage(v) << std::endl;
}

void foreach_learn()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    for (auto i : v)
    {
        std::cout << i << std::endl;
    }

    // 这里遍历时昂贵的，因为word每次都会复制一个副本出来，所以可以使用const std::string &word
    std::vector<std::string> v2{"hello", "world", "ohayou"};
    for (std::string word : v2)
    {
        std::cout << word << std::endl;
    }

    // 在该循环的每次迭代中，word将绑定到下一个数组元素。这允许访问数组元素的值，而不必制作昂贵的副本
    for (const std::string &word : v2)
    {
        std::cout << word << std::endl;
    }

    // 可以自动推导
    for (const auto &word : v2)
    {
        std::cout << word << std::endl;
    }

    // 什么时候使用 auto，auto& 或 const auto&
    // 通常，可廉价复制类型使用auto，需要更改的对象使用 auto&， 昂贵地复制类型使用 const auto&。但对于基于范围的for循环，许多开发人员认为最好始终使用const auto&，因为它更经得起未来的考验。
}

void resize_arr()
{
    std::vector<int> v1{1, 2, 3, 4, 5}; // 5个元素
    std::cout << v1.size() << std::endl;

    v1.resize(10); // 运行时调整大小
    std::cout << v1.size() << std::endl;
    for (const auto &i : v1)
    {
        std::cout << i << std::endl;
    }
    std::cout << v1.capacity() << std::endl;
}

void printStack(const std::vector<int> &stack)
{
    if (stack.empty()) // if stack.size == 0
        std::cout << "Empty";

    for (const int &element : stack)
        std::cout << element << ' ';

    // \t 是tab, 帮助对齐
    std::cout << "\tCapacity: " << stack.capacity() << "  Length " << stack.size() << "\n";
}

class Foo
{
private:
    std::string m_a{};
    int m_b{};

public:
    Foo(std::string_view a, int b)
        : m_a{a}, m_b{b}
    {
    }

    explicit Foo(int b)
        : m_a{}, m_b{b} {};
};

void vector_stack()
{
    std::vector<int> stack{};
    stack.push_back(1);
    stack.push_back(2);
    printStack(stack); // 1 2  Capacity: 2  Length 2
    stack.pop_back();
    printStack(stack); //  1  Capacity: 2  Length 1

    // 这种在初始化时指定长度得会改变容量和长度大小
    std::vector<int> stack2(3);
    printStack(stack2); //  0 0 0  Capacity: 3  Length 3

    // 指定容量大小
    std::vector<int> stack3{};
    stack3.reserve(6);
    printStack(stack3); //  Capacity: 6  Length 0

    std::vector<Foo> stack4{};
    Foo foo{"hello", 1};
    stack4.push_back(foo); // 与emplace_back一样，但是更推荐使用push_back
    stack4.emplace_back(foo);

    // 添加临时元素,这里只能使用emplace_back
    /**
     * 使用emplace_back()，我们不需要创建要传递的临时对象。相反，我们传递将用于创建临时对象的参数，
     * 并将它们转发（使用称为完美转发的功能）到vector，
     * 在vector中，它们用于创建和初始化vector内的对象。这避免了本应制作的副本
     *
     * push_back()不会使用显式构造函数，而emplace_back()会。这使得emplace_back更加危险，
     * 因为很容易意外地调用显式构造函数来执行一些没有意义的转换。
     */
    stack4.emplace_back("world", 2);
}